<?php
// +-------------------------------------------------------------------------+
// | PHP Version 5                                                           |
// +-------------------------------------------------------------------------+
// | ImaEngine - web development framework                                   |
// | Copyright (c) 2006-2007 Sardar Yumatov <ja.doma@gmail.com>              |
// +-------------------------------------------------------------------------+
// | This library is free software; you can redistribute it and/or modify it |
// | under the terms of the GNU Lesser General Public License as published   |
// | by the Free Software Foundation; either version 2.1 of the License, or  |
// | (at your option) any later version.                                     |
// |                                                                         |
// | This library is distributed in the hope that it will be useful, but     |
// | WITHOUT ANY WARRANTY; without even the implied warranty of              |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    |
// | See the GNU Lesser General Public License for more details.             |
// |                                                                         |
// | You should have received a copy of the GNU Lesser General Public        |
// | License along with this library; if not, write to the Free Software     |
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
// +-------------------------------------------------------------------------+

/**
* Cacheable templater implementation.
*
* This templater doesn't compile source template, but parses it by each request. The caller should
* use caching to store the generated content. In other words this templater is the best solution
* for modules that doesn't change the content very often, such as static page documents.
*
* If you template generates different content on each request, such as news listing or alike, then
* use compilable templater instead.
*
* @author Sardar Yumatov <ja.doma@gmail.com>
* @copyright Copyright &copy; 2006-2007 Sardar Yumatov
* @version v1.0
* @package common.template
* @category Templater
*
* @PHP5only
*/

if(!defined('PROCESS')) {
	header('HTTP/1.1 404 Not Found');
	die('NotFound');
}

require_once('lib/GenericExceptions.php');
require_once('lib/FileUtils.php');
require_once('lib/template/AbstractTemplateDocument.php');
require_once('lib/template/TemplateParser.php');

/**
 * Template document loader.
 * This class fully loads the template to obtain all defined blocks. Then it starts
 * template execution.
 *
 * This class doesn't implement any naming scheme. Use this class if you know the path to the template file,
 * otherwise use a high-level wrapper instead.
 *
 * @author Sardar Yumatov <ja.doma@gmail.com>
 */
class ParsedTemplateDocument extends AbstractTemplateDocument {

	/**
	 * The template source file.
	 * @var FilePath
	 */
	private $file;

	/**
	 * Load given template $file.
	 * @throws NotFoundException if template file isn't found
	 * @throws SyntaxException on any syntax error
	 * @throws IOException on any error in the underlying IO layer
	 * @param string $name the template name, is not used by this class
	 * @param string|FilePath $file the file to load
	 */
	public function __construct($name, $file) {
		parent::__construct($name);
		$this->file = FilePath::createPath($file, true);
		$handler = $this->file->getHandler();
		if(!$handler->exists()) throw new NotFoundException("Given template file '{$this->file->getPath(true)}' isn't found!");
		$cont = $handler->getFileContents(); //throws IOException
		//parse content
		$grabber = new DefaultTemplateParserHandler();
		TemplateParser::parse($grabber, $cont); //throws SyntaxException

		$this->blocks = array();
		$this->rootblock = new ParsedTemplateDocumentBlock($grabber->getRootBlock(), $this);
		foreach($grabber->getBlocks() as $block)
			$this->blocks[$block->getName()] = new ParsedTemplateDocumentBlock($block, $this, $this->blocks);
	}

	/**
	 * Returns path to the template source file
	 * @return FilePath
	 */
	public function getPath() {
		return $this->file;
	}
}

/**
 * Block evaluator.
 * This is just a simple wrapper over the parser grabber block.
 * @author Sardar Yumatov <ja.doma@gmail.com>
 */
class ParsedTemplateDocumentBlock extends AbstractTemplateDocumentBlock {

	/**
	 * The block body.
	 * @var array
	 */
	private $body;

	/**
	 * Construct new block
	 * @param DefaultTemplateParserHandlerBlock $block actual data container
	 */
	public function __construct(DefaultTemplateParserHandlerBlock $block, ParsedTemplateDocument $template, &$blocks=null) {
		if(!is_null($parent = $block->getParentBlock())) { //child block
			if(is_null($name = $parent->getName())) $parent = $template->getRootBlock();
			elseif(isset($blocks[$name])) $parent = $blocks[$name];
			else $parent = $blocks[$name] = new ParsedTemplateDocumentBlock($parent, $template, $blocks);
		} else $parent = null;
		parent::__construct($block->getName(), $parent, $template);
		$this->pre_filter_chain = $block->getFilters();
		$this->body = $block->getBody();
	}


	/**
	 * Generate the text output of this block.
	 * @param TemplateDocumentHandler $handler template handler
	 * @return void the text content should be send to output buffer
	 */
	public function generateOutput(TemplateDocumentHandler $handler) {
		foreach($this->body as $entry) {
			if(is_string($entry)) echo $entry;
			else { //process a tag
				ob_start();
				if(!$handler->processTag($this, $entry['action'], $entry['markers'])) ob_end_clean();
				else { //use content
					if(empty($entry['filters'])) ob_end_flush();
					else { //apply filters
						$cont = ob_get_clean();
						foreach($entry['filters'] as $name=>$params)
							$cont = AbstractTemplateDocument::executeFilter($name, $cont, $params);
						echo $cont;
					}
				}
			}
		}
	}
}
?>